package org.Yiran.timetale_re.item.machine.base;

import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.IEnergyStorage;
import org.Yiran.timetale_re.energy.power.ForgeEnergyStorage;
import org.Yiran.timetale_re.item.base.DescriptiveItem;
import org.Yiran.timetale_re.util.item.TooltipUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

/**
 * 储能电池基础类
 * <p>
 * 其他电池类型可以继承此类并自定义容量和其他属性。
 * 提供完整的能量存储、传输、显示和工具提示功能。
 * </p>
 */
public abstract class EnergyStorageItem extends DescriptiveItem {

    // ============================== 常量定义 ==============================

    /** 能量条最大宽度（像素） */
    private static final int ENERGY_BAR_MAX_WIDTH = 13;

    // ============================== 实例变量 ==============================

    /** 电池容量 */
    protected final int capacity;
    /** 最大接收速率 */
    protected final int maxReceive;
    /** 最大提取速率 */
    protected final int maxExtract;
    /** 电池等级名称键 */
    protected final String tierNameKey;
    /** 能量条颜色 */
    protected final int barColor = 0x39FF67;
    /** 当前最大接收速率 */
    protected int currentMaxReceive;
    /** 当前最大提取速率 */
    protected int currentMaxExtract;

    // ============================== 构造方法 ==============================

    /**
     * 构造函数 - 完整参数版本
     * <p>
     * 完整指定所有参数，包括容量、接收速率、提取速率、物品属性、等级名称和颜色
     * </p>
     *
     * @param capacity 电池容量
     * @param maxReceive 最大接收速率
     * @param maxExtract 最大提取速率
     * @param properties 物品属性
     * @param tierNameKey 电池等级名称键
     */
    public EnergyStorageItem(int capacity, int maxReceive, int maxExtract, Item.Properties properties, String tierNameKey) {
        super(properties);
        this.capacity = capacity;
        this.maxReceive = maxReceive;
        this.maxExtract = maxExtract;
        this.currentMaxReceive = maxReceive;
        this.currentMaxExtract = maxExtract;
        this.tierNameKey = tierNameKey;
    }

    // ============================== 能量操作方法 ==============================

    /**
     * 获取当前存储的能量
     * <p>
     * 优先从NBT标签中读取能量值，如果不存在则返回0
     * </p>
     *
     * @param stack 电池物品堆
     * @return 当前存储的能量
     */
    public int getEnergyStored(ItemStack stack) {
        if (stack.hasTag()) {
            CompoundTag tag = stack.getTag();
            if (tag != null) {
                return tag.getInt("Energy");
            }
        }
        return 0;
    }

    /**
     * 设置存储的能量
     * <p>
     * 将能量值保存到物品的NBT标签中，确保值在有效范围内（0到容量之间）
     * </p>
     *
     * @param stack 电池物品堆
     * @param energy 要设置的能量值
     */
    public void setEnergyStored(ItemStack stack, int energy) {
        initializeTagIfNeeded(stack);
        if (stack.getTag() != null) {
            stack.getTag().putInt("Energy", Math.max(0, Math.min(energy, capacity)));
        }
    }

    /**
     * 向电池中充入能量
     * <p>
     * 根据当前最大接收速率和电池剩余容量计算实际可接收的能量
     * </p>
     *
     * @param stack 电池物品堆
     * @param maxReceive 最大充入能量
     * @param simulate 是否为模拟模式（不实际修改能量值）
     * @return 实际充入的能量
     */
    public int receiveEnergy(ItemStack stack, int maxReceive, boolean simulate) {
        // 从NBT中读取当前设置的速率（如果存在）
        int currentReceive = getCurrentReceiveRate(stack);

        int energyStored = getEnergyStored(stack);
        int limit = capacity - energyStored;
        int received = Math.min(maxReceive, Math.min(currentReceive, limit));

        if (!simulate) {
            setEnergyStored(stack, energyStored + received);
        }

        return received;
    }

    /**
     * 从电池中提取能量
     * <p>
     * 根据当前最大提取速率和电池存储能量计算实际可提取的能量
     * </p>
     *
     * @param stack 电池物品堆
     * @param maxExtract 最大提取能量
     * @param simulate 是否为模拟模式（不实际修改能量值）
     * @return 实际提取的能量
     */
    public int extractEnergy(ItemStack stack, int maxExtract, boolean simulate) {
        // 从NBT中读取当前设置的速率（如果存在）
        int currentExtract = getCurrentExtractRate(stack);

        int energyStored = getEnergyStored(stack);
        int limit = Math.min(maxExtract, Math.min(currentExtract, energyStored));

        if (!simulate) {
            setEnergyStored(stack, energyStored - limit);
        }

        return limit;
    }

    // ============================== 能量操作辅助方法 ==============================

    /**
     * 获取当前接收速率
     * <p>
     * 优先从物品NBT中读取自定义速率，如果不存在则使用默认速率
     * </p>
     *
     * @param stack 电池物品堆
     * @return 当前接收速率
     */
    private int getCurrentReceiveRate(ItemStack stack) {
        int currentReceive = this.currentMaxReceive;
        if (stack.hasTag() && stack.getTag() != null && stack.getTag().contains("MaxReceive")) {
            currentReceive = stack.getTag().getInt("MaxReceive");
        }
        return currentReceive;
    }

    /**
     * 获取当前提取速率
     * <p>
     * 优先从物品NBT中读取自定义速率，如果不存在则使用默认速率
     * </p>
     *
     * @param stack 电池物品堆
     * @return 当前提取速率
     */
    private int getCurrentExtractRate(ItemStack stack) {
        int currentExtract = this.currentMaxExtract;
        if (stack.hasTag() && stack.getTag() != null && stack.getTag().contains("MaxExtract")) {
            currentExtract = stack.getTag().getInt("MaxExtract");
        }
        return currentExtract;
    }

    // ============================== 能量操作检查方法 ==============================

    /**
     * 检查是否可以提取能量
     *
     * @return 是否可以提取
     */
    public boolean canExtract() {
        return maxExtract > 0;
    }

    /**
     * 检查是否可以接收能量
     *
     * @return 是否可以接收
     */
    public boolean canReceive() {
        return maxReceive > 0;
    }

    // ============================== 能量条显示方法 ==============================

    /**
     * 是否显示能量条
     * <p>
     * 始终返回true，确保能量条始终显示
     * </p>
     *
     * @param stack 电池物品堆
     * @return true表示显示能量条
     */
    @Override
    public boolean isBarVisible(@NotNull ItemStack stack) {
        return true;
    }

    /**
     * 获取能量条显示宽度
     * <p>
     * 根据当前能量与总容量的比例计算能量条宽度
     * </p>
     *
     * @param stack 电池物品堆
     * @return 能量条显示宽度（像素）
     */
    @Override
    public int getBarWidth(@NotNull ItemStack stack) {
        return calculateEnergyBarWidth(stack);
    }

    /**
     * 获取能量条颜色
     * <p>
     * 返回预设的能量条颜色值
     * </p>
     *
     * @param stack 电池物品堆
     * @return 颜色值
     */
    @Override
    public int getBarColor(@NotNull ItemStack stack) {
        return barColor;
    }

    /**
     * 计算能量条宽度
     * <p>
     * 根据当前能量与总容量的比例计算能量条宽度
     * </p>
     *
     * @param stack 电池物品堆
     * @return 能量条显示宽度（像素）
     */
    private int calculateEnergyBarWidth(ItemStack stack) {
        int energyStored = getEnergyStored(stack);
        if (energyStored <= 0) {
            return 0;
        }
        return Math.max(1, Math.round(ENERGY_BAR_MAX_WIDTH * (float) energyStored / (float) capacity));
    }

    // ============================== 工具提示方法 ==============================

    /**
     * 添加工具提示信息
     * <p>
     * 根据是否按住Shift键决定显示简要信息还是详细信息
     * </p>
     *
     * @param stack 物品堆
     * @param level 世界对象
     * @param tooltip 工具提示列表
     * @param flag 提示标志
     */
    @Override
    @OnlyIn(Dist.CLIENT)
    public void appendHoverText(@NotNull ItemStack stack, @Nullable Level level, @NotNull List<Component> tooltip, @NotNull TooltipFlag flag) {
        int energyStored = getEnergyStored(stack);
        int displayMaxReceive = this.currentMaxReceive;
        if (stack.hasTag() && stack.getTag() != null) {
            if (stack.getTag().contains("MaxReceive")) {
                displayMaxReceive = stack.getTag().getInt("MaxReceive");
            }
        }

        // 基础描述
        TooltipUtils.addEnergyStorageLevel(tooltip, this.getDescriptionId(), tierNameKey);

        // 隔一行
        TooltipUtils.addSeparatorLine(tooltip);

        if (!TooltipUtils.isShiftPressed()) {
            // 提示按Shift查看详细信息
            TooltipUtils.addShiftPrompt(tooltip);
        } else {

            // 显示当前存储信息
            TooltipUtils.addEnergyStorageInfo(tooltip, this.getDescriptionId(), energyStored, capacity, displayMaxReceive);
        }
    }

    // ============================== 能力系统方法 ==============================

    /**
     * 初始化能力提供者
     * <p>
     * 为物品提供Forge Energy能力支持
     * </p>
     *
     * @param stack 物品堆
     * @param nbt NBT数据
     * @return 能力提供者
     */
    @Override
    public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag nbt) {
        return new EnergyCapabilityProvider(stack);
    }

    // ============================== 辅助方法 ==============================

    /**
     * 初始化标签（如果需要）
     * <p>
     * 如果物品堆没有标签，则创建一个新的标签
     * </p>
     *
     * @param stack 物品堆
     */
    private void initializeTagIfNeeded(ItemStack stack) {
        if (!stack.hasTag()) {
            stack.setTag(new CompoundTag());
        }
    }

    /**
     * 检查是否应该触发重新装备动画
     * <p>
     * 当能量值发生变化时返回true，以触发物品栏刷新
     * </p>
     *
     * @param oldStack 旧物品堆
     * @param newStack 新物品堆
     * @param slotChanged 槽位是否改变
     * @return 是否应该触发重新装备动画
     */
    @Override
    public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
        // 检查能量是否发生变化，如果变化则触发重新装备动画（会导致物品栏刷新）
        return getEnergyStored(oldStack) != getEnergyStored(newStack) || super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged);
    }

    // ============================== 内部类 ==============================

    /**
     * 能量能力提供者
     * <p>
     * 实现物品的Forge Energy能力支持
     * </p>
     */
    private class EnergyCapabilityProvider implements ICapabilityProvider {
        private final LazyOptional<IEnergyStorage> energyStorage;

        /**
         * 构造函数
         *
         * @param stack 物品堆
         */
        public EnergyCapabilityProvider(ItemStack stack) {
            this.energyStorage = LazyOptional.of(() -> new ForgeEnergyStorage(capacity, maxReceive, maxExtract, getEnergyStored(stack)) {
                @Override
                public int receiveEnergy(int maxReceive, boolean simulate) {
                    return EnergyStorageItem.this.receiveEnergy(stack, maxReceive, simulate);
                }

                @Override
                public int extractEnergy(int maxExtract, boolean simulate) {
                    return EnergyStorageItem.this.extractEnergy(stack, maxExtract, simulate);
                }

                @Override
                public int getEnergyStored() {
                    return EnergyStorageItem.this.getEnergyStored(stack);
                }

                @Override
                public int getMaxEnergyStored() {
                    return EnergyStorageItem.this.capacity;
                }

                @Override
                public boolean canExtract() {
                    return EnergyStorageItem.this.canExtract();
                }

                @Override
                public boolean canReceive() {
                    return EnergyStorageItem.this.canReceive();
                }
            });
        }

        @Override
        public @NotNull <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
            if (cap == ForgeCapabilities.ENERGY) {
                return energyStorage.cast();
            }
            return LazyOptional.empty();
        }
    }
}